package com.tczs.web.util;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author:zhao-baolin
 * @Description: 生成公钥私钥
 * @Date:Created in 2018/7/4
 * @Modified By:
 */
public class RSACoder {
    private static Logger logger = Logger.getLogger(RSACoder.class);

    //采用RSA算法
    public static final String KEY_ALGORITHM = "RSA";
    public static final Map<String,Object> keys = new HashMap<>();
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /* RSA最大加密明文大小 */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /* RSA最大解密密文大小 */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 初始化密钥对
     * @return
     * @throws Exception
     */
    public static void initKey() {
        //获得对象 KeyPairGenerator 参数 RSA 1024个字节
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            logger.error("init RSA keys is error",e);
        }
        keyPairGen.initialize(1024);
        //通过对象 KeyPairGenerator 获取对象KeyPair
        KeyPair keyPair = keyPairGen.generateKeyPair();

        //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //公私钥对象存入map中
        keys.put(PUBLIC_KEY, publicKey);
        keys.put(PRIVATE_KEY, privateKey);
    }

    /**
     * 获得公钥
     * @return
     * @throws Exception
     */
    public static String getPublicKey(){
        Key key = (Key) keys.get(PUBLIC_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 获得私钥
     * @return
     * @throws Exception
     */
    public static String getPrivateKey() {
        Key key = (Key) keys.get(PRIVATE_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 编码成base64
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptBASE64(byte[] key) {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    private static PublicKey getPublicKey(String publicKey) {
        PublicKey key = null;
        try {
            byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            key = keyFactory.generatePublic(keySpec);
        }catch (Exception e){
            logger.error("get PublicKey error",e);
        }
        return key;
    }

    private static PrivateKey getPrivateKey(String privateKey) {
        PrivateKey key = null;
        try {
            byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(privateKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            key = keyFactory.generatePrivate(keySpec);
        }catch (Exception e){
            logger.error("get PrivateKey error",e);
        }
        return key;
    }

    /**
     * 使用给定的公钥加密明文
     *
     * @param publicKey 给定的公钥。
     * @param plaintext 明文
     * @return 给定字符串的密文。
     */
    public static String encryptString(String publicKey, String plaintext) {
        if(publicKey == null || plaintext == null)
            return null;
        PublicKey key = getPublicKey(publicKey);
        if (key == null)
            return null;
        byte[] data = plaintext.getBytes();
        try {
            byte[] en_data = encrypt(key, data);
            return Base64.encodeBase64String(en_data);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 使用给定的私钥解密密文。
     *
     * @param privateKey   给定的私钥
     * @param encryptText 密文
     * @return 原文字符串。
     */
    public static String decryptString(String privateKey, String encryptText) {
        if(privateKey == null || encryptText == null)
            return null;
        PrivateKey key = getPrivateKey(privateKey);
        if (key == null)
            return null;
        byte[] en_data = Base64.decodeBase64(encryptText);
        byte[] data = decrypt(key, en_data);
        return new String(data);
    }

    /**
     * 使用指定的公钥加密数据。
     *
     * @param key 给定的公钥。
     * @param data      要加密的数据。
     * @return 加密后的数据。
     */

    public static byte[] encrypt(Key key, byte[] data) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            Cipher ci = Cipher.getInstance(KEY_ALGORITHM);
            ci.init(Cipher.ENCRYPT_MODE, key);
            int inputLen = data.length;
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = ci.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = ci.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            return encryptedData;
        }catch (Exception e){
            logger.error("encrypt data is error",e);
        }finally {
            StreamUtil.close(out);
        }
        return null;
    }

    /**
     * 使用指定的钥匙解密数据。
     *
     * @param key 指定的钥匙
     * @param data      要解密的数据
     * @return 原数据
     * @throws Exception
     */
    public static byte[] decrypt(Key key, byte[] data) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            Cipher ci = Cipher.getInstance(KEY_ALGORITHM);
            ci.init(Cipher.DECRYPT_MODE, key);
            int inputLen = data.length;
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = ci.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = ci.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            return decryptedData;
        }catch (Exception e){
            logger.error("decrypt data is error",e);
        }finally {
            StreamUtil.close(out);
        }
        return null;
    }

    public static void main(String[] args) {
//        initKey();
//        String publicKey = getPublicKey();
//        System.out.println(publicKey);
//        String privateKey = getPrivateKey();
//        System.out.println(privateKey);
//        String text = "哈哈哈哈哈";
//        String encrypt = encryptString(publicKey, text);
//        System.out.println("公钥加密后---------------");
//        System.out.println(encrypt);
        String encrypt = "ZxTmLQ3wiT4aosrdiceaQgEKJsWEL5qw3zkfzaN9/UMphgyjcsh1Ubh%2B3eHTCzgTJshhZrTv9HJ5HmutjhUwnCe4EwapqedgyPQz%2BrtjnfilcmkZZLQ7bNtCKZZB/tT9Yph6A/1NroBVmhYz/tJuOcPukcGfRfOctGFWeAfxdkg=";
        String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALO3yobIWNJFyZSy060RfXG4g5X2\n" +
                "VljfFux7sRhuOTrcwSbYI00FCaVOIl9jz+4/JUiSNWZwWlPBqZKRpndwURz2nhAq9iNhFOOFVD5+\n" +
                "9qN6RcCGpHLeF9118EnhkLYVxgK7F1sKDetz78wvvZ3eBvppDoy4ozM3oJAsi5UkH0nTAgMBAAEC\n" +
                "gYAUVtOT526t9Xn32zk7mAQjb6dsvGFPU4groWAMjMTrVXqCo+ly2QQbcjlPpGMu6nSUvv9OJZ8D\n" +
                "ZaKEVPmLAOoc9WCbB6f5TCOVjkqS4+c1rQQCfEk7cHkvEvHceSNX2Q5WhFxxmOAV+2zUSshnB61q\n" +
                "wYqKTWjKiN9YjXr1NpaTqQJBAOUC0h6pto5kRjP4MUVjEde4PBwaTryOBneuU/h1pyjZvA4RvHBM\n" +
                "Q6AHmmp1/0iufYaGwM2Gv+fpbPfqTZKcBk0CQQDI5dDd+pdF3x3+wo5LsYruZM5w+B3KVM4v9kEM\n" +
                "54cGY4qLGEJdXZnDLHDgw7VcWHA4HkEQqRorg1C8vaCQhOCfAkEAg+mqCsuLQl30Kj9Ocj9sqauP\n" +
                "xEhLDkVo6EtcRwPOM7OnpRsGRlFHqsh2Y9hwIfeQiorYSArUQ8urXupxoH+LCQJBALzgDAnWokhH\n" +
                "p1ywL7YMepD853YgX2mtZ5iCDdCDEFg6V7PEIAjFil2Lt6gpd6YVY5PvJb7zLS6Bq/N22LFmrAMC\n" +
                "QAUWUrZuKa8ydamfhTpZRdIixQ7zpfgVJsCUPGyhqIvI08zNE0GIHL1SCckvdWorv3DuLXJWTXH9\n" +
                "78ACvWYz8Yw=";
        String decryptString = decryptString(privateKey, encrypt);
        System.out.println("私钥解密后---------------");
        System.out.println(decryptString);
    }
}

